﻿using FySystem.Job.JobTask;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace FySystem.Job
{
    public class FyJobContext
    {
        /// <summary>
        /// 任务执行委托
        /// </summary>
        /// <param name="currTime"></param>
        public delegate void FyJobAction(DateTime currTime);

        /// <summary>
        /// 定时任务列表
        /// </summary>
        private static List<JobBase> _lstJobs = new List<JobBase>();

        /// <summary>
        /// 添加待执行任务
        /// </summary>
        /// <param name="job"></param>
        public static void AddJob(JobBase job)
        {
            try
            {
                if (job == null)
                    throw new Exception("传入定时任务信息为空~");

                lock (_lstJobs)
                {
                    _lstJobs.Add(job);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 开始执行
        /// </summary>
        public static void Run()
        {
            try
            {
                //判断定时任务的必要字段是否为空
                foreach(var job in _lstJobs)
                {
                    if (job.JobExecuter == null)
                        throw new Exception($"{job.JobName}：传入任务执行函数为空~");
                    if (job.ExecuteTrigger == null)
                        throw new Exception($"{job.JobName}：任务ExecuteTrigger为空~");
                    if (job.NextExecuteTimeGenerator == null)
                        throw new Exception($"{job.JobName}：任务NextExecuteTimeGenerator为空~");
                }

                Task tsk = new Task(new Action(() =>
                {
                    ScanJobs();
                }));
                tsk.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 线程方法，扫描任务并执行
        /// </summary>
        private static void ScanJobs()
        {
            try
            {
                Stopwatch sw = new Stopwatch();
                const int SPLIT_TIME = 50;                  //50ms扫描一次
                while (true)
                {
                    //50ms轮询一次，Start之后不允许动态添加任务，所以这里线程安全，不需要加锁
                    if (_lstJobs.Count == 0)
                    {
                        Thread.Sleep(SPLIT_TIME);
                        continue;
                    }

                    sw.Restart();

                    //遍历所有定时任务
                    foreach(var item in _lstJobs)
                    {
                        try
                        {
                            if (item.NextExecuteTime == null)
                                item.NextExecuteTime = item.GetNextExecuteTime();

                            if (item.ExecuteJudge())
                            {
                                //如果判断下来需要执行
                                item.NextExecuteTime = item.GetNextExecuteTime();          //生成下次执行时间

                                //通过线程执行任务，因为任务可能会很耗时
                                Task task = new Task(() =>
                                {
                                    item.JobExecuter?.Invoke(DateTime.Now);
                                });
                                task.Start();
                            }
                        }
                        catch { }
                    }

                    sw.Stop();

                    //如果执行时间不满50ms，休眠
                    int deltaTime = SPLIT_TIME - (int)sw.ElapsedMilliseconds;
                    if (deltaTime > 0)
                        Thread.Sleep(deltaTime);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}
